package com.csaba.connector.bha;

import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;

import com.csaba.connector.DummyServer;
import com.csaba.connector.I18NServiceException;
import com.csaba.connector.ServiceException;
import com.csaba.connector.bha.model.BHABank;
import com.csaba.connector.bha.model.BHACustomer;
import com.csaba.connector.bha.model.BHASession;
import com.csaba.connector.bha.model.ErrorCodeResolver;
import com.csaba.connector.model.Session;
import com.csaba.connector.service.AbstractLoginService;
import com.csaba.connector.service.AccountHistoryService;
import com.csaba.connector.service.AccountService;
import com.csaba.util.I18N;
import com.csaba.util.LoggerHelper;
import com.ind.simplenode.SimpleNode;

/**
 * @author Gabe
 * FIXME implement ChangePasswordService
 */
public class BHALoginService extends AbstractLoginService
{
	private final static Logger logger = LoggerHelper.getDefaultLogger();

	public final static int PASSWORD_LOGINID_LENGTH = 6;
	public final static int TOKEN_LOGINID_LENGTH = 8;
	public final static int PASSWORD_MAX_LENGTH = 20;
	public final static int TOKEN_LENGTH = 6;

	private final static Pattern passwordLoginIdPattern = Pattern.compile("[a-z]{2}[0-9]{2}[a-z]{2}"); //$NON-NLS-1$
	private final static Pattern tokenLoginIdPattern = Pattern.compile("[0-9]{8}"); //$NON-NLS-1$

	public final static int AUTH_TYPE_UNKNOWN = 0;
	public final static int AUTH_TYPE_PASSWORD = 1;
	public final static int AUTH_TYPE_TOKEN = 2;

	@Override
	public void execute( Session session ) throws ServiceException
	{
		if ( session == null || session.getCustomer() != null )
			session = new BHASession();

		final BHASession local = (BHASession) session;
		//Create login request
		final SimpleNode loginRequest = new SimpleNode();
		loginRequest.add("loginId", customer.getLoginId());
		loginRequest.add("credential", customer.getPassword());
		loginRequest.setPath("userAgentInfo.screen", "1024*768*32");
		loginRequest.setPath("userAgentInfo.jvmVersion", System.getProperty("java.version"));
		loginRequest.setPath("userAgentInfo.msJvmVersion", "");
		loginRequest.setPath("userAgentInfo.os", System.getProperty("os.name") + " " + System.getProperty("os.version")
				+ "(" + System.getProperty("os.arch") + ")");
		final String userAgent = System.getProperty("user.agent", "N/A");
		loginRequest.setPath("userAgentInfo.userAgent", userAgent);

		// call login
		final SimpleNode loginReply = local.sendRequest("Login", "login", loginRequest, new ErrorCodeResolver()
		{
			@Override
			public String resolveRC( final int rc, final String rcText )
			{
				if ( rc == -5 || rc == -200 )
					return I18N.getString(BHABank.getInstance(), "msg.invalidPassword");
				return super.resolveRC(rc, rcText);
			}
		});

		final boolean blocked = loginReply.getBoolean("blocked");
		if ( blocked )
		{
			throw new I18NServiceException(BHABank.getInstance(), "msg.blockedUser", "", customer.getLoginId());
		}

		//		boolean authenticated = loginReply.getBoolean( "authneticated" );
		final boolean mustChangePassword = loginReply.getBoolean("mustChangePassword");
		if ( mustChangePassword )
		{
			//TODO use PasswordExpiredException here once password change is implemented.
			throw new I18NServiceException(BHABank.getInstance(), "msg.expiryingAccount");
		}
		//		boolean recorderUser = loginReply.getBoolean( "recorderUser" );
		//		boolean tokenAuthMode = loginReply.getBoolean( "tokenAuthMode" );
		//		boolean servicesNote = loginReply.getBoolean( "servicesNote" );
		//		int timeout = loginReply.getInt( "timeout" );
		//		int maxImportFileLength = loginReply.getInt( "maxImportFileLength" );
		//		int loginStatusId = loginReply.exists( "loginStatusId" ) ? loginReply.getInt( "loginReplyId" ) : 0;
		final String userName = loginReply.getString("loginUserName");
		final SimpleNode[] customers = loginReply.getNodeArray("customers");

		// select customer
		final SimpleNode setCustomerRequest = new SimpleNode();
		setCustomerRequest.add("selectedCustomerId", customers[0].getString("customerId")); //TODO always the first customer Id is used. No cap selection is possible.
		final SimpleNode setCustomerReply = local.sendRequest("Login", "setCustomerId", setCustomerRequest, null);
		final SimpleNode[] functions = setCustomerReply.getNodeArray("functions");

		if ( logger.isLoggable(Level.FINE) )
		{
			logger.fine("Following functions are enabled:");
			for ( final SimpleNode function : functions )
			{
				logger.fine(" - " + function.getString("functionId") + " (authentication "
						+ ( function.getBoolean("needAuthorization") ? "is" : "isn't" ) + " required)");
			}
		}

		customer = new BHACustomer(customer, setCustomerReply.getNode("customerData"));
		customer.addPermission(AccountService.PERMISSION);
		customer.addPermission(AccountHistoryService.PERMISSION);

		local.setCustomer(customer);
		logger.info("User " + userName + " is logging in with customer " + local.getCustomer().getName() + " ("
				+ local.getCustomer().getId() + ")...");

		this.session = session;
		DummyServer.getInstance().addSession(session);
	}

	public static int detectAuthType( final String loginId )
	{
		int authType = AUTH_TYPE_UNKNOWN;

		final int loginLength = loginId.length();

		if ( loginLength == PASSWORD_LOGINID_LENGTH )
		{ //password based
			final boolean match = passwordLoginIdPattern.matcher(loginId).find();
			if ( match )
			{
				authType = AUTH_TYPE_PASSWORD;
			}
		}
		else if ( loginLength == TOKEN_LOGINID_LENGTH )
		//token based
		{
			final boolean match = tokenLoginIdPattern.matcher(loginId).find();
			if ( match )
			{
				authType = AUTH_TYPE_TOKEN;
			}
		}
		return authType;
	}
}
